#include "database_preparedstatement.h"

#include "log.h"
#include "report.h"
#include "database_preparedstatement_mysql.h"
#include "database_query_result.h"
#include "database_mysql_connection.h"

namespace afcore {
namespace database {

CPreparedStatementBase::CPreparedStatementBase(uint32_t index, uint8_t capacity)
  : index_(index)
  , statement_data_(capacity) {
}

CPreparedStatementBase::~CPreparedStatementBase() { }

void CPreparedStatementBase::SetNull(uint8_t index) {
  DBG_ASSERT(index < statement_data_.size());
  statement_data_[index].type = kPreparedStatementValueType_Null;
}

void CPreparedStatementBase::SetBool(uint8_t index, const bool value) {
  DBG_ASSERT(index < statement_data_.size());
  statement_data_[index].data.boolean = value;
  statement_data_[index].type = kPreparedStatementValueType_Bool;
}

void CPreparedStatementBase::SetUInt8(uint8_t index, uint8_t value) {
  DBG_ASSERT(index < statement_data_.size());
  statement_data_[index].data.ui8 = value;
  statement_data_[index].type = kPreparedStatementValueType_Ui8;
}

void CPreparedStatementBase::SetUInt16(uint8_t index, uint16_t value) {
  DBG_ASSERT(index < statement_data_.size());
  statement_data_[index].data.ui16 = value;
  statement_data_[index].type = kPreparedStatementValueType_Ui16;
}

void CPreparedStatementBase::SetUInt32(uint8_t index, uint32_t value) {
  DBG_ASSERT(index < statement_data_.size());
  statement_data_[index].data.ui32 = value;
  statement_data_[index].type = kPreparedStatementValueType_Ui32;
}

void CPreparedStatementBase::SetUInt64(uint8_t index, uint64_t value) {
  DBG_ASSERT(index < statement_data_.size());
  statement_data_[index].data.ui64 = value;
  statement_data_[index].type = kPreparedStatementValueType_Ui64;
}

void CPreparedStatementBase::SetInt8(uint8_t index, int8_t value) {
  DBG_ASSERT(index < statement_data_.size());
  statement_data_[index].data.i8 = value;
  statement_data_[index].type = kPreparedStatementValueType_I8;
}

void CPreparedStatementBase::SetInt16(uint8_t index, int16_t value) {
  DBG_ASSERT(index < statement_data_.size());
  statement_data_[index].data.i16 = value;
  statement_data_[index].type = kPreparedStatementValueType_I16;
}

void CPreparedStatementBase::SetInt32(uint8_t index, int32_t value) {
  DBG_ASSERT(index < statement_data_.size());
  statement_data_[index].data.i32 = value;
  statement_data_[index].type = kPreparedStatementValueType_I32;
}

void CPreparedStatementBase::SetInt64(uint8_t index, int64_t value) {
  DBG_ASSERT(index < statement_data_.size());
  statement_data_[index].data.i64 = value;
  statement_data_[index].type = kPreparedStatementValueType_I64;
}

void CPreparedStatementBase::SetFloat(uint8_t index, const float value) {
  DBG_ASSERT(index < statement_data_.size());
  statement_data_[index].data.f = value;
  statement_data_[index].type = kPreparedStatementValueType_Float;
}

void CPreparedStatementBase::SetDouble(uint8_t index, const double value) {
  DBG_ASSERT(index < statement_data_.size());
  statement_data_[index].data.d = value;
  statement_data_[index].type = kPreparedStatementValueType_Double;
}

void CPreparedStatementBase::SetString(uint8_t index, const std::string &value) {
  DBG_ASSERT(index < statement_data_.size());
  statement_data_[index].binary.resize(value.length() + 1);
  memcpy(statement_data_[index].binary.data(), value.c_str(), value.length() + 1);
  statement_data_[index].type = kPreparedStatementValueType_String;
}

void CPreparedStatementBase::SetBinary(uint8_t index, const std::vector<uint8_t> &value) {
  DBG_ASSERT(index < statement_data_.size());
  statement_data_[index].binary = value;
  statement_data_[index].type = kPreparedStatementValueType_Binary;
}

void CPreparedStatementBase::BindParameters(CPreparedStatementMysql *stmt) {
  DBG_ASSERT(stmt);
  stmt_ = stmt;

  uint8_t i = 0;
  uint8_t len = static_cast<uint8_t>(statement_data_.size());
  for (; i < len; ++i) {
    switch(statement_data_[i].type) {
      case kPreparedStatementValueType_Bool:
        {
          stmt_->SetBool(i, statement_data_[i].data.boolean);
        }
        break;
      case kPreparedStatementValueType_Ui8:
        {
          stmt_->SetUInt8(i, statement_data_[i].data.ui8);
        }
        break;
      case kPreparedStatementValueType_Ui16:
        {
          stmt_->SetUInt16(i, statement_data_[i].data.ui16);
        }
        break;
      case kPreparedStatementValueType_Ui32:
        {
          stmt_->SetUInt32(i, statement_data_[i].data.ui32);
        }
        break;
      case kPreparedStatementValueType_Ui64:
        {
          stmt_->SetUInt64(i, statement_data_[i].data.ui64);
        }
        break;
      case kPreparedStatementValueType_I8:
        {
          stmt_->SetInt8(i, statement_data_[i].data.i8);
        }
        break;
      case kPreparedStatementValueType_I16:
        {
          stmt_->SetInt16(i, statement_data_[i].data.i16);
        }
        break;
      case kPreparedStatementValueType_I32:
        {
          stmt_->SetInt32(i, statement_data_[i].data.i32);
        }
        break;
      case kPreparedStatementValueType_I64:
        {
          stmt_->SetInt64(i, statement_data_[i].data.i64);
        }
        break;
      case kPreparedStatementValueType_Float:
        {
          stmt_->SetFloat(i, statement_data_[i].data.f);
        }
        break;
      case kPreparedStatementValueType_Double:
        {
          stmt_->SetDouble(i, statement_data_[i].data.d);
        }
        break;
      case kPreparedStatementValueType_String:
        {
          stmt_->SetBinary(i, statement_data_[i].binary, true);
        }
        break;
      case kPreparedStatementValueType_Binary:
        {
          stmt_->SetBinary(i, statement_data_[i].binary, false);
        }
        break;
      case kPreparedStatementValueType_Null:
        {
          stmt_->SetNull(i);
        }
        break;
    }
  }
#if defined(AFCORE_DEBUG)
  if (i < stmt->param_count_) {
    LOG_WARN("BindParameters() for statement {} did not bind all allocated parameters", index_);
  }
#endif
}

CPreparedStatementTask::CPreparedStatementTask(CPreparedStatementBase* stmt, bool async)
  : stmt_(stmt) {
  has_result_ = async; // 如果异步 会有结果
  if (async) {
    result_ = new RPreparedQueryResultPromise();
  }
}

CPreparedStatementTask::~CPreparedStatementTask() {
  delete stmt_;
  if (has_result_ && nullptr != result_) {
    delete result_;
  }
}

bool CPreparedStatementTask::Execute() {
  if (has_result_) {
    CPreparedResultSet* result = conn_->Query(stmt_);
    if (!result || !result->GetRowCount()) {
      delete result;
      result_->set_value(RPreparedQueryResultSptr(nullptr));
      return false;
    }
    result_->set_value(RPreparedQueryResultSptr(result));
    return true;
  }
  return conn_->Execute(stmt_);
}

} // !namespace database
} // !namespace afcore